package cn.itcast.nems.studentrecord.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.lang.Assert;
import cn.itcast.ic.common.bean.Page;
import cn.itcast.ic.common.exception.v2.BizExceptionProducer;
import cn.itcast.nems.common.PageQueryModel;
import cn.itcast.nems.common.account.AccountHolder;
import cn.itcast.nems.common.constant.OrderTypeEnum;
import cn.itcast.nems.common.enumeration.NemsErrorBodyEnum;
import cn.itcast.nems.common.tenant.TenantInfo;
import cn.itcast.nems.common.tenant.TenantInfoHolder;
import cn.itcast.nems.common.util.NumberUtil;
import cn.itcast.nems.common.utils.WrapperUtil;
import cn.itcast.nems.journal.dto.JournalRenewalCountDTO;
import cn.itcast.nems.journal.service.JournalRenewalPaidService;
import cn.itcast.nems.manager.msas.MsasManager;
import cn.itcast.nems.manager.redis.RedisUtil;
import cn.itcast.nems.manager.redis.constant.RedisKeyConstant;
import cn.itcast.nems.manager.util.CustomBeanUtil;
import cn.itcast.nems.studentrecord.constant.OrderSourceEnum;
import cn.itcast.nems.studentrecord.dao.condition.StudentRecordParams;
import cn.itcast.nems.studentrecord.dao.dataobject.ClazzEnrollmentCountDO;
import cn.itcast.nems.studentrecord.dao.dataobject.StudentRecordCountDO;
import cn.itcast.nems.studentrecord.dao.dataobject.StudentRecordDO;
import cn.itcast.nems.studentrecord.dao.entity.ClazzEnrollmentCount;
import cn.itcast.nems.studentrecord.dao.entity.StudentRecord;
import cn.itcast.nems.studentrecord.dao.mapper.ClazzEnrollmentCountMapper;
import cn.itcast.nems.studentrecord.dto.ClazzEnrollmentCountDTO;
import cn.itcast.nems.studentrecord.dto.StudentRecordDTO;
import cn.itcast.nems.studentrecord.dto.StudentRecordQueryDTO;
import cn.itcast.nems.studentrecord.dto.studentrecord.StudentRecordMessageDTO;
import cn.itcast.nems.studentrecord.enumeration.StudentRecordErrorBodyEnum;
import cn.itcast.nems.studentrecord.service.ClazzEnrollmentCountService;
import cn.itcast.nems.studentrecord.service.StudentRecordCountService;
import cn.itcast.nems.studentrecord.service.studentrecord.StudentRecordService;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

@Service
@RequiredArgsConstructor
@Slf4j
public class ClazzEnrollmentCountServiceImpl extends ServiceImpl<ClazzEnrollmentCountMapper, ClazzEnrollmentCount>
        implements ClazzEnrollmentCountService {
    private final JournalRenewalPaidService journalRenewalPaidService;
    private final StudentRecordCountService studentRecordCountService;
    private final StudentRecordService studentRecordService;
    private final RedisUtil redisUtil;
    private final Environment environment;
    private final MsasManager msasManager;

    public String getProfiles() {
        String[] activeProfiles = environment.getActiveProfiles();
        log.info("【缴费详情】获取环境参数：{}", JSON.toJSONString(activeProfiles));
        return activeProfiles[0].toUpperCase();
    }

    @Override
    public Page<ClazzEnrollmentCountDTO> findCountPageList(PageQueryModel<ClazzEnrollmentCountDTO> queryModel) {
        com.baomidou.mybatisplus.extension.plugins.pagination.Page<ClazzEnrollmentCountDO> page = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>();
        page.setCurrent(queryModel.getPageNumber());
        page.setSize(queryModel.getPageSize());
        ClazzEnrollmentCountDO countDO = CustomBeanUtil.copyProperties(queryModel.getCondition(), new ClazzEnrollmentCountDO());
        // 没有过滤用户组权限 且用户组为空 ，返回空数据
        if  ((Objects.isNull(countDO.getExcludeFlag() ) ||  !countDO.getExcludeFlag() )&&  !AccountHolder.hasEmployeeGroups()){
            return Page.of(queryModel.getPageNumber(), queryModel.getPageSize(), 0, null);
        }
        countDO.setEmployeeGroups(AccountHolder.getEmployeeGroups());
        IPage<ClazzEnrollmentCountDO> pageData = getBaseMapper().findPageList(page, countDO);
        List<ClazzEnrollmentCountDTO> list = pageData.getRecords().stream().map(this::transferClazzEnrollmentCountDTO).toList();
        return Page.of(Long.valueOf(pageData.getCurrent()).intValue(),
                Long.valueOf(pageData.getSize()).intValue(),
                Long.valueOf(pageData.getTotal()).intValue(), list);
    }

    private ClazzEnrollmentCountDTO transferClazzEnrollmentCountDTO(ClazzEnrollmentCountDO countDO) {
        ClazzEnrollmentCountDTO dto = CustomBeanUtil.copyProperties(countDO, new ClazzEnrollmentCountDTO());
        dto.setBalanceAmountDue(NumberUtil.convertToYuan(countDO.getBalanceAmountDue()));
        return dto;
    }

    @Override
    public Page<StudentRecordDTO> findPageListByClazzEnrollmentCountId(String id, PageQueryModel<StudentRecordQueryDTO> queryModel) {
        Assert.isTrue(Objects.nonNull(queryModel) && Objects.nonNull(queryModel.getCondition()), BizExceptionProducer.badRequest("参数不能为空! "));
        Assert.notBlank(id, BizExceptionProducer.badRequest("缴费情况ID不能为空! "));
        com.baomidou.mybatisplus.extension.plugins.pagination.Page<ClazzEnrollmentCountDO> page = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>();
        page.setCurrent(queryModel.getPageNumber());
        page.setSize(queryModel.getPageSize());
        ClazzEnrollmentCount enrollmentCount = getBaseMapper().selectById(id);
        Assert.notNull(enrollmentCount, BizExceptionProducer.badRequest("ID:s%, 缴费情况不存在! ", id));
        queryModel.getCondition().setClazzIds(List.of(enrollmentCount.getClazzId()));
        return studentRecordService.pageQuery(queryModel);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void calculatePaidCount(String recordId) {
        log.info("【消费学籍支付消息】汇总缴费详情部分支付、完全支付人数 ， 学籍ID：{}", recordId);
        StudentRecord studentRecord = studentRecordService.getById(recordId);
        Assert.notNull(studentRecord, BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST, "学籍ID：" + recordId));
        // 统计升级凭单缴费情况
        if (OrderSourceEnum.RENEWAL.name().equalsIgnoreCase(studentRecord.getSource())) {
            JournalRenewalCountDTO renewalCountDTO = journalRenewalPaidService.findJournalIds(studentRecord.getId());
            log.info("【消费学籍支付消息】查询学籍ID对应凭单的所有学籍列表：{}", JSON.toJSONString(renewalCountDTO.getRecordIds()));
            StudentRecordParams recordParams = new StudentRecordParams(studentRecord.getClazzId(), OrderTypeEnum.ALL_PAID.name(), renewalCountDTO.getRecordIds(), null, null);
            List<StudentRecordDO> list = studentRecordCountService.findStudentRecordListByCondition(recordParams);
            log.info("【消费学籍支付消息】查询学籍ID对应凭单的完成缴费学籍列表：{}", JSON.toJSONString(list));
            journalRenewalPaidService.updatePaidNumber(renewalCountDTO.getJournalRenewalId(), CollectionUtil.isEmpty(list) ? 0 : list.size());
        }
    }

    private void enrollmentCountByClazzId(ClazzEnrollmentCount enrollmentCount, Integer clazzId) {
        StudentRecordCountDO studentRecordCountDO = studentRecordCountService.findCountByClazzId(clazzId);
        Assert.notNull(studentRecordCountDO, BizExceptionProducer.serverError("班级ID：{}，不存在学籍记录", clazzId));
        enrollmentCount.setPayingNumber(studentRecordCountDO.getPayingNumber());
        enrollmentCount.setPaidNumber(studentRecordCountDO.getPaidNumber());
        enrollmentCount.setPendingNumber(studentRecordCountDO.getPendingNumber());
        enrollmentCount.setTotalNumber(studentRecordCountDO.getTotalNumber());
        enrollmentCount.setBalanceAmountDue(studentRecordCountDO.getBalanceAmountDue());
        // 如果完全支付人数等于  总人数 ， 将缴费情况状态设置为 ”已完成缴费“
        if (enrollmentCount.getTotalNumber() > 0) {
            enrollmentCount.setState(Objects.equals(enrollmentCount.getPaidNumber(), enrollmentCount.getTotalNumber()) ? 1 : 0);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void createRecordToTotal(StudentRecordMessageDTO message) {
        try {
            AccountHolder.setAccount("admin");
            TenantInfoHolder.setTenantInfo(new TenantInfo(message.getTenantId(), message.getSubtenantId()));
            Assert.notNull(message.getStudentRecordId(), BizExceptionProducer.produce(NemsErrorBodyEnum.PARAM_EMPTY, "学籍ID"));
            StudentRecord studentRecord = studentRecordService.getById(message.getStudentRecordId());
            Assert.notNull(studentRecord, BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST, "学籍ID：" + message.getStudentRecordId()));
            ClazzEnrollmentCount enrollmentCount = this.checkAndInsert(studentRecord.getClazzId());
            enrollmentCount.setPendingNumber(enrollmentCount.getPendingNumber() + 1);
            enrollmentCount.setTotalNumber(enrollmentCount.getTotalNumber() + 1);
            int updateResult = getBaseMapper().updateById(enrollmentCount);
            Assert.isTrue(updateResult > 0,
                    BizExceptionProducer.produce(NemsErrorBodyEnum.DATABASE_UPDATE_ERROR, "缴费情况ID：" + enrollmentCount.getId()));
            log.info("【缴费详情统计】监听学籍创建消息,班级ID：{}, 缴费情况：{},学籍详情：{}", studentRecord.getClazzId(), JSON.toJSONString(enrollmentCount), JSON.toJSONString(studentRecord));
        } finally {
            AccountHolder.clear();
            TenantInfoHolder.clear();
        }
    }

    @Override
    public synchronized ClazzEnrollmentCount checkAndInsert(Integer clazzId) {
        ClazzEnrollmentCount enrollmentCount = getBaseMapper().selectOne(new WrapperUtil<ClazzEnrollmentCount>()
                .limitLaseOne("created_datetime").eq(ClazzEnrollmentCount::getClazzId, clazzId));
        log.info("[维护缴费情况汇总]查询缴费情况详情: {}", JSON.toJSONString(enrollmentCount));
        if (Objects.isNull(enrollmentCount)) {
            enrollmentCount = new ClazzEnrollmentCount();
            enrollmentCount.setClazzId(clazzId);
            enrollmentCount.setPayingNumber(0);
            enrollmentCount.setPaidNumber(0);
            enrollmentCount.setPendingNumber(0);
            enrollmentCount.setTotalNumber(0);
            enrollmentCount.setState(0);
            int result = getBaseMapper().insert(enrollmentCount);
            Assert.isTrue(result > 0, BizExceptionProducer.produce(NemsErrorBodyEnum.DATABASE_INSERT_ERROR, "缴费情况汇总"));
            log.info("[维护缴费情况汇总] 持久化缴费情况详情: {}", JSON.toJSONString(enrollmentCount));
        }
        return enrollmentCount;
    }

    public String manualRefresh(Boolean executeFlag) {
        String datetimeKey = CustomBeanUtil.append("|", RedisKeyConstant.NEMS_ENROLMENT_COUNT_JOB_DATETIME, getProfiles());
        try {
            if (Objects.nonNull(executeFlag) && executeFlag) {
                this.refresh(false);
                msasManager.sendWechatMessage(AccountHolder.getRequiredAccount(), "【新EMS】您发起的“同步缴费情况”任务执行完毕，执行结果：成功，请登录系统查看");
            }
        } catch (Exception e) {
            msasManager.sendWechatMessage(AccountHolder.getRequiredAccount(), "新EMS】您发起的“同步缴费情况”任务执行完毕，执行结果：失败，请重新发起");
            BizExceptionProducer.throwProduce(e, StudentRecordErrorBodyEnum.ENROLLMENT_COUNT_EXECUTE_ERROR);
        }
        Object redisTime = redisUtil.get(datetimeKey);
        return Objects.nonNull(redisTime) ? String.valueOf(redisTime).replace("-", "/") : null;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<Integer> refresh(Boolean isAll) {
        String startTime = LocalDateTimeUtil.formatNormal(LocalDateTime.now());
        String datetimeKey = CustomBeanUtil.append("|", RedisKeyConstant.NEMS_ENROLMENT_COUNT_JOB_DATETIME, getProfiles());
        String executeFlagKey = CustomBeanUtil.append("|", RedisKeyConstant.NEMS_ENROLMENT_COUNT_EXECUTE_FLAG, getProfiles());
        Assert.isFalse(redisUtil.hasKey(executeFlagKey), BizExceptionProducer.produce(StudentRecordErrorBodyEnum.ENROLLMENT_COUNT_EXECUTE_ERROR));
        try {
            redisUtil.set(executeFlagKey, true, 10 * 60);
        } catch (Exception e) {
            redisUtil.del(executeFlagKey);
            BizExceptionProducer.throwProduce(e, NemsErrorBodyEnum.REDIS_ERROR);
        }
        Object redisTime = redisUtil.get(datetimeKey);
        log.info("【定时任务】参数 isAll：{}，redisTime：{}", isAll, JSON.toJSONString(redisTime));
        List<StudentRecordCountDO> countList = studentRecordCountService.findAllClazzEnrolmentCount(isAll, Objects.nonNull(redisTime) ?
                LocalDateTimeUtil.parse(String.valueOf(redisTime), "yyyy-MM-dd HH:mm:ss").minusHours(2) : null);
        log.info("【定时任务】统计缴费情况 查询近一天列表：{}", JSON.toJSONString(countList));
        countList = Objects.requireNonNullElse(countList, new ArrayList<>());
        List<Integer> clazzIds = new ArrayList<>();
        for (StudentRecordCountDO item : countList) {
            ClazzEnrollmentCount enrollmentCount = this.checkAndInsert(item.getClazzId());
            this.enrollmentCountByClazzId(enrollmentCount, item.getClazzId());
            log.info("【定时任务】统计缴费情况详情：{}", JSON.toJSONString(enrollmentCount));
            this.updateById(enrollmentCount);
            clazzIds.add(item.getClazzId());
        }
        redisUtil.set(datetimeKey, startTime);
        redisUtil.del(executeFlagKey);
        return clazzIds;
    }
}
